package org.mysterylab.utopiaframework.web.filter;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

/**
 * 对 URL 进行 REST 风格的重写, 构造出没有扩展名的 REST URL.<br>
 * 在 web.xml 中设置了 default servlet 的过滤为 /static/, 这样就为静态资源加入
 * /static/ 的前缀, 可以减轻 Tomcat 服务器的负担, 有点类似 Apache 干的活.<br>
 * 在 web.xml 中的设置如下:
 * <pre>
 * {@code
 *  <filter>
 * 	    <filter-name>restUrlRewriteFilter</filter-name>
 * 	    <filter-class>org.mysterylab.utopiaframework.web.filter.RestUrlRewriteFilter</filter-class>
 * 	    <init-param>
 * 	        <param-name>prefix</param-name>
 * 			<param-value>/static</param-value>
 * 		</init-param>
 * 		<init-param>
 * 			<param-name>excludeExtentions</param-name>
 * 			<param-value>jsp,jspx,do,json,xml</param-value>
 * 		</init-param>
 * 		<init-param>
 * 			<param-name>debug</param-name>
 * 			<param-value>true</param-value>
 * 		</init-param>
 * 	</filter>
 * 	<filter-mapping>
		<filter-name>restUrlRewriteFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
	<!-- Tomcat 默认的Servlet -->
	<servlet-mapping>
		<servlet-name>default</servlet-name>
		<url-pattern>/static/*</url-pattern>
	</servlet-mapping>
 * }
 * </pre>
 * 
 * @author zhouych
 */
public class RestUrlRewriteFilter extends OncePerRequestFilter implements Filter {
	
	private Logger logger = LoggerFactory.getLogger(RestUrlRewriteFilter.class);

	/**
	 * 默认的不需要过滤的后缀名, 这些后缀名表示该文件属于动态文件
	 */
	private static final String DEFAULT_EXECUDE_EXTENTIONS = "jsp,jspx,do";
	
	/**
	 * 默认的prefix
	 */
	private static final String DEFAULT_PREFIX = "/static";
	
	/**
	 * 需要被添加的前缀, 默认为"/static"
	 */
	private String prefix;
	
	/**
	 * 是否输出 debug 信息, 如果用于生产可将其设置为 false
	 */
	private boolean debug = false;
	
	private Set<String> excludeExtentions = new HashSet<String>();
	
	private String[] excludePrefixes = new String[0];

	@Override
	protected void initFilterBean() throws ServletException {
		try {
			initParameter(getFilterConfig());
		} catch (IOException e) {
			throw new ServletException("init paramerter error", e);
		}
	}
	
	/**
	 * 初始化各种参数
	 * 
	 * @param filterConfig
	 * @throws IOException
	 */
	private void initParameter(FilterConfig filterConfig) throws IOException {
		
		prefix = getStringParameter(filterConfig, "prefix", DEFAULT_PREFIX);
		debug = getBooleanParameter(filterConfig, "debug", false);
		String excludeExtentionsString = getStringParameter(filterConfig,
				"excludeExtentions", DEFAULT_EXECUDE_EXTENTIONS);
		excludeExtentions = new HashSet<String>((Arrays
				.asList(excludeExtentionsString.split(","))));

		String excludePrefixsString = getStringParameter(filterConfig,
				"excludePrefixes", null);
		if (StringUtils.hasText(excludePrefixsString)) {
			excludePrefixes = excludePrefixsString.split(",");
		}

		logger.info("RestUrlRewriteFilter.prefix {}  will rewrite url from /demo.html => ${prefix}/demo.html by forward", prefix);
		logger.info("RestUrlRewriteFilter.excludeExtentions=[{}] will not rewrite url",
				excludeExtentionsString);
		logger.info("RestUrlRewriteFilter.excludePrefixes=[{}] will not rewrite url",
				excludePrefixsString);
		logger.info("RestUrlRewriteFilter.debug={}", debug);
	}
	
	@Override
	protected void doFilterInternal(HttpServletRequest request,
			HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		String from = request.getRequestURI().substring(
				request.getContextPath().length());
		if (rewriteURL(from)) {
			final String to = prefix + from;
			if (debug) {
				logger.debug("RestUrlRewriteFilter: forward request from {} to {}",
						from, to);
			}
			request.getRequestDispatcher(to).forward(request, response);
		} else {
			if (debug) {
				logger.debug("RestUrlRewriteFilter: not rewrite url: {}",
						request.getRequestURI());
			}
			filterChain.doFilter(request, response);
		}
	}

	/**
	 * 判断是否需要重写 url
	 * 
	 * @param from
	 *            请求的 URL
	 * @return
	 */
	private boolean rewriteURL(String from) {
		
		// 根据文件后缀名来进行判断重写规则
		String extension = StringUtils.getFilenameExtension(from);
		if (org.apache.commons.lang.StringUtils.isEmpty(extension)) {
			return false;
		}

		for (String excludePrefix : excludePrefixes) {
			if (from.startsWith(excludePrefix)) {
				return false;
			}
		}
		
		if (excludeExtentions.contains(extension)) {
			return false;
		}
		
		if (from.indexOf("/dwr/") != -1) {
			/*
			 * 如果是 DWR-3.0 自动生成的 js 文件，则也不需要进行重写
			 * 比如：/dwr/engine.js、/dwr/util.js、/dwr/interface/service.js 等
			 */
			return false;
		}
		return true;
	}

	private String getStringParameter(FilterConfig filterConfig, String name,
			String defaultValue) {
		
		String value = filterConfig.getInitParameter(name);
		if (org.apache.commons.lang.StringUtils.isEmpty(value)) {
			return defaultValue;
		}
		return value;
	}

	private boolean getBooleanParameter(FilterConfig filterConfig, String name,
			boolean defaultValue) {
		
		String value = getStringParameter(filterConfig, name, String
				.valueOf(defaultValue));
		return Boolean.parseBoolean(value);
	}
}
